{
 this file is part of Ares
 Aresgalaxy ( http://aresgalaxy.sourceforge.net )

  This program is free software; you can redistribute it and/or
  modify it under the terms of the GNU General Public License
  as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*****************************************************************
 The following delphi code is based on Emule (0.46.2.26) Kad's implementation http://emule.sourceforge.net
 and KadC library http://kadc.sourceforge.net/
*****************************************************************
 }

{
Description:
DHT high level routines related to searches
}

unit dhtsearchManager;

interface

uses
  classes, classes2, int128, dhtconsts, dhtutils, dhttypes, dhtsearch, sysutils;

procedure findNode(id: pCU_INT128);
procedure findNodeComplete(id: pCU_Int128);
function alreadySearchingFor(target: pCU_INT128): boolean;
procedure processResponse(target: pCU_INT128; fromIP: cardinal; fromPort: word;
          results: tmylist; garbageList: TMylist);
procedure CheckSearches(nowt: cardinal); //every second
procedure PublishHash(phash: precord_DHT_hashfile);
procedure processPublishHashAck(target: pCU_INT128; fromip: cardinal; fromporT: word);
function num_searches(ttype: dhttypes.tdhtsearchtype): integer;
procedure processPublishKeyAck(publishID: pCU_INT128; fromip: cardinal; fromPort: word);
procedure SendPublishKeyFiles(s: TDHTSearch; TargetIP: cardinal; TargetPort: word);
function has_KeywordSearchWithID(ID: word): boolean;
procedure ClearContacts(list: TMylist);


implementation

uses
  vars_global, helper_datetime, helper_ipfunc, securehash,
  windows, helper_strings, dhtSocket, mysupernodes, dhtcontact;   


function has_KeywordSearchWithID(ID: word): boolean;
var
  i: integer;
  s: tDHTsearch;
begin
  result := false;

  for i := 0 to DHT_Searches.count - 1 do begin
    s := DHT_Searches[i];
    if s.m_type <> dhttypes.KEYWORD then continue;
    if s.m_searchID <> ID then continue;
    result := true;
    exit;
  end;

end;


function num_searches(ttype: dhttypes.tdhtsearchtype): integer;
var
  i: integer;
  s: TDHTSearch;
begin
  result := 0;

  for i := 0 to DHT_Searches.count - 1 do begin
    s := DHT_Searches[i];
    if s.m_type = ttype then inc(result);
  end;

end;

procedure SendPublishKeyFiles(s: TDHTSearch; TargetIP: cardinal; TargetPort: word);
var
  i, offset: integer;
  lenItem: word;
  payload: string;
begin
  offset := 0;

  for i := 0 to s.m_publishKeyPayloads.count - 1 do begin

    if offset = 0 then begin
      CU_INT128_CopyToBuffer(@s.m_target, @DHT_Buffer[2]);
      move(vars_global.myport, DHT_Buffer[18], 2);
      inc(offset, 20);
    end;

    payload := s.m_publishKeyPayloads.strings[i];
    lenItem := length(payload);
    move(lenItem, DHT_Buffer[offset], 2);
    inc(offset, 2);
    move(payload[1], DHT_Buffer[offset], lenItem);
    inc(offset, lenItem);

    if offset >= 9000 then begin
      DHT_Len_tosend := offset;
      DHT_Buffer[1] := CMD_DHT_PUBLISHKEY_REQ;
      DHT_Send(TargetIP, TargetPort, True);
      offset := 0;
    end;
  end;

  if offset > 20 then begin
    DHT_Len_tosend := offset;
    DHT_Buffer[1] := CMD_DHT_PUBLISHKEY_REQ;
    DHT_Send(TargetIP, TargetPort, (DHT_len_tosend > 200));
  end;

end;

procedure PublishHash(phash: precord_DHT_hashfile);
var
  s: tDHTsearch;
  id: CU_INT128;

begin

  CU_INT128_CopyFromBuffer(@phash^.hashValue[0], @id);
  if alreadySearchingFor(@id) then exit;

  s := TDHTsearch.create;
  s.m_type := DHTtypes.STOREFILE;

  CU_INT128_Fill(@s.m_target, @id);
  setLength(s.m_outPayload, 26);
  move(phash^.hashValue[0], s.m_outPayload[1], 20);
  move(vars_global.LANIPC, s.m_outPayload[21], 4);
  move(vars_global.myport, s.m_outPayload[25], 2);

    // add up to 5 supernodes (len=30)
  if vars_global.im_firewalled then
    s.m_outPayload := s.m_outPayload + mysupernodes.mysupernodes_serialize;


  DHT_Searches.add(s);
  s.startIDSearch;
end;

procedure ClearContacts(list: TMylist);
var
  c: TContact;
begin
  while (list.count > 0) do begin
    c := list[list.count - 1];
    list.delete(list.count - 1);
    c.free;
  end;
end;

procedure processResponse(target: pCU_INT128; fromIP: cardinal; fromPort: word;
  results: tmylist; garbageList: TMylist);
var
  s: tDHTsearch;
  i: integer;
  found: boolean;
begin
  found := false;
  s := nil;

  for i := 0 to DHT_searches.count - 1 do begin
    s := DHT_searches[i];
    if CU_INT128_Compare(@s.m_target, target) then begin
      found := true;
      break;
    end;
  end;

  if not found then begin
    ClearContacts(GarbageList);
    exit;
  end;

  s.processResponse(fromIP, fromPort, results);
end;


procedure processPublishKeyAck(publishID: pCU_INT128; fromip: cardinal; fromPort: word);
var
  s: TDHTSearch;
  i: integer;
begin

  for i := 0 to DHT_Searches.count - 1 do begin
    s := DHT_Searches[i];
    if s.m_type <> dhttypes.STOREKEYWORD then continue;
    if not CU_INT128_Compare(@s.m_target, publishID) then continue;

    inc(s.m_answers);
    break;
  end;

end;

procedure processPublishHashAck(target: pCU_INT128; fromip: cardinal; fromporT: word);
var
  s: TDHTSearch;
  i: integer;
begin

  for i := 0 to DHT_Searches.count - 1 do begin
    s := DHT_Searches[i];
    if s.m_type <> dhttypes.STOREFILE then continue;
    if CU_INT128_Compare(@s.m_target, target) then begin
      inc(s.m_answers);

      break;
    end;
  end;

end;



function alreadySearchingFor(target: pCU_INT128): boolean;
var
  i: integer;
  s: tDHTsearch;
begin
  result := False;

  for i := 0 to DHT_Searches.count - 1 do begin
    s := DHT_Searches[i];
    if CU_INT128_Compare(@s.m_target, target) then begin
      result := true;
      exit;
    end;
  end;

end;

procedure findNodeComplete(id: pCU_Int128);
var
  s: tDHTsearch;
begin
  if alreadySearchingFor(id) then exit;

  s := TDHTsearch.create;
  s.m_type := DHTtypes.NODECOMPLETE;
  CU_INT128_fill(@s.m_target, id);
  DHT_Searches.add(s);
  s.startIDSearch;
end;

procedure findNode(id: pCU_INT128);
var
  s: tDHTsearch;
begin
  if alreadySearchingFor(id) then exit;

  s := TDHTsearch.create;
  s.m_type := DHTtypes.NODE;
  CU_INT128_fill(@s.m_target, id);
  DHT_Searches.add(s);
  s.StartIDSearch;
end;

procedure CheckSearches(nowt: cardinal); //every second
var
  i: integer;
  s: TDHTSearch;
begin

  i := 0;
  while (i < DHT_Searches.count) do begin
    s := DHT_Searches[i];

    case s.m_type of


      KEYWORD: begin
          if s.m_created + SEARCHKEYWORD_LIFETIME < nowt then begin
            DHT_Searches.delete(i);
            s.free;
            continue;
          end;

          if ((s.m_answers >= SEARCHKEYWORD_TOTAL) or
            (s.m_created + SEARCHKEYWORD_LIFETIME - SEC(20) < nowt)) then begin
            s.expire;
            inc(i);
            continue;
          end;
          s.CheckStatus;
        end;


      FINDSOURCE: begin
          if s.m_created + SEARCHFINDSOURCE_LIFETIME < nowt then begin
            DHT_Searches.delete(i);
            s.free;
            continue;
          end;

          if ((s.m_answers >= SEARCHFINDSOURCE_TOTAL) or
            (s.m_created + SEARCHFINDSOURCE_LIFETIME - SEC(20) < nowt)) then begin
            s.expire;
            inc(i);
            continue;
          end;

          s.CheckStatus;
        end;

      NODE: begin
          if s.m_created + SEARCHNODE_LIFETIME < nowt then begin
            DHT_Searches.delete(i);
            s.free;
            continue;
          end;
          s.CheckStatus;
        end;

      NODECOMPLETE: begin
          if s.m_created + SEARCHNODE_LIFETIME < nowt then begin
            DHT_m_Publish := true;
            DHT_Searches.delete(i);
            s.free;
            continue;
          end;

          if ((s.m_created + SEARCHNODECOMP_LIFETIME < nowt) and
            (s.m_answers >= SEARCHNODECOMP_TOTAL)) then begin
            DHT_m_Publish := true;
            DHT_Searches.delete(i);
            s.free;
            continue;
          end;
          s.CheckStatus;

        end;

      STOREFILE: begin
          if s.m_created + SEARCHSTOREFILE_LIFETIME < nowt then begin
            DHT_Searches.delete(i);
            s.free;
            continue;
          end;

          if ((s.m_answers >= SEARCHSTOREFILE_TOTAL) or
            (s.m_created + SEARCHSTOREFILE_LIFETIME - SEC(20) < nowt)) then begin
            s.expire;
            inc(i);
            continue;
          end;

          s.CheckStatus;
        end;

      STOREKEYWORD: begin
          if s.m_created + SEARCHSTOREKEYWORD_LIFETIME < nowt then begin
            DHT_Searches.delete(i);
            s.free;
            continue;
          end;

          if ((s.m_answers >= SEARCHSTOREKEYWORD_TOTAL) or
            (s.m_created + SEARCHSTOREKEYWORD_LIFETIME - SEC(20) < nowt)) then begin
            s.expire;
            inc(i);
            continue;
          end;

          s.CheckStatus;

        end else begin
        if s.m_created + SEARCH_LIFETIME < nowt then begin
          DHT_Searches.delete(i);
          s.free;
          continue;
        end;

        s.CheckStatus;
      end;
    end;

    inc(i);
  end;

end;


end.


